/**
 *
 * @author Alex & Umit
 */
public class CircularList implements PositionList{

    private Node tail;
    private int size;

    public CircularList(){
        size = 0;
        tail = null;
    }

    public int size(){
        return size;
    }

    public boolean isEmpty(){
        return size == 0;
    }

    public Node checkPosition(Position p) throws InvalidPositionException{
        Node node = tail;
        boolean found = false;
        if(!isEmpty()){
            if(p != null){
                while(node.next() != tail && !found){
                    if(node.next() == p){
                        node = node.next();
                        found = true;
                    }
                    else{
                        node = node.next();
                    }
                }
            }
            else throw new InvalidPositionException();
        }
        return node;
    }

    public Position first(){
        if(!isEmpty()){
            return tail.next();
        }
        else return null;
    }

    public Position last(){
        return tail;
    }

    public Position next(Position p) throws InvalidPositionException, BoundaryViolationException{
        Node node = checkPosition(p);
        if(node != null){
            node = node.next();
        }
        else throw new BoundaryViolationException();
        return node;
    }

    public Position prev(Position p) throws InvalidPositionException, BoundaryViolationException{
        if(p == null)
            throw new InvalidPositionException();
        Node node = tail;
        Boolean found = false;
        if(!isEmpty()){
            node = node.next();
            while(node != null && !found){
                if(node.next() == p){
                    found = true;
                }
                else node = node.next();
            }
        }
        if(found)
            return node;
        else return null;
    }

    public void addFirst(Object o){
        Node node = new Node(o, null);
        if(isEmpty()){
            tail = node;
            tail.setNext(tail);
        }
        else{
            Node next = tail.next();
            node.setNext(next);
            tail.setNext(node);
        }
        size++;
    }

    public void addLast(Object o){
        addFirst(o);
        tail = tail.next();
    }

    public void addAfter(Position p, Object o) throws InvalidPositionException{
        Node previous = checkPosition(p);
        Node insert = new Node(o, null);
        if(previous != null){
            insert.setNext(previous.next());
            previous.setNext(insert);
            if(previous == tail){
                tail = insert;
            }
            size++;
        }
        else throw new InvalidPositionException();
    }

    public void addBefore(Position p, Object o) throws InvalidPositionException{
        Node previous = (Node)prev(p);
        Node next = (Node)next(p);
        Node insert = new Node(o, next);
        previous.setNext(insert);
        size++;
    }

    public Object remove(Position p) throws InvalidPositionException{
        Node res = checkPosition(p);
        Node previous = (Node)prev(p);
        Node next = (Node)next(p);
        previous.setNext(next);
        size--;
        return res.element();
    }

    public Object set(Position p, Object o){
        Node node = checkPosition(p);
        Object res = node.element();
        node.setElement(o);
        return res;

    }

    @Override
    public String toString(){
        String res = "<CircularList[";
        Node node = tail;
        if(!isEmpty()){
            res += node.next() + ",";
            node = node.next();
            while(node != tail){;
                res += node.next().toString() + ",";
                node = node.next();
            }
        }
        if(!isEmpty()){
            res = res.substring(0, res.length() - 1);
        }

        return res += "]>";
    }

}